package com.example.config.security;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import java.util.Collections;

/**
 * 授权服务
 * oauth2.0认证授权类型
 * 账号密码登录：AuthenticationManager
 * 授权码登录：AuthorizationCodeServices
 * 隐式授权模式：ImplicitGrantService（已过时）
 * TokenGranter：认证授权模式完全由程序掌控
 * 简化模式
 */
@Configuration
@EnableAuthorizationServer
public class DemoAuthorizationServer extends AuthorizationServerConfigurerAdapter {

    protected final TokenStore tokenStore;
    protected final PasswordEncoder passwordEncoder;
    protected final AuthenticationManager authenticationManager;
    protected final AuthorizationCodeServices authorizationCodeServices;
    protected final ClientDetailsService clientDetailService;
    protected final JwtAccessTokenConverter jwtAccessTokenConverter;

    public DemoAuthorizationServer(@Qualifier("jwtTokenStore") TokenStore tokenStore,
                                   PasswordEncoder passwordEncoder,
                                   AuthenticationManager authenticationManager,
                                   AuthorizationCodeServices authorizationCodeServices,
                                   ClientDetailsService clientDetailService,
                                   @Qualifier("accessTokenConverter") JwtAccessTokenConverter jwtAccessTokenConverter) {
        super();
        this.tokenStore = tokenStore;
        this.passwordEncoder = passwordEncoder;
        this.authenticationManager = authenticationManager;
        this.authorizationCodeServices = authorizationCodeServices;
        this.clientDetailService = clientDetailService;
        this.jwtAccessTokenConverter = jwtAccessTokenConverter;
    }

    /**
     * 用来配置客户端详情服务（ClientDetailService）
     * 客户端详情信息在这里进行初始化
     * 可以把客户端详情信息写死在这里或者通过数据库来存储调取详情信息
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("c1")
                .secret(passwordEncoder.encode("secret"))
                .resourceIds("res1")
                .authorizedGrantTypes("authorization_code", "password",
                        "client_credentials", "implicit", "refresh_token")
                .scopes("all") // 仅作为标识使用
                .autoApprove(false) // 设置为false自定跳转回调地址
                .redirectUris("https://www.baidu.com"); // 回调地址
    }

    /**
     * 用来配置令牌的访问端点和令牌服务
     *
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager) // 密码模式
                .authorizationCodeServices(authorizationCodeServices) // 授权码模式
                .tokenServices(tokenServices(jwtAccessTokenConverter)) // token模式
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);
    }

    /**
     * 用来配置令牌端点的安全约束
     * /oauth/authorize 授权端点
     * /oauth/token 令牌端点
     * /oauth/confirm_access 用户确认授权提交端点
     * /oauth/error 授权服务错误信息端点
     * /oauth/check_token 用于资源服务访问的令牌解析端点
     * /oauth/token_key 提供公有密钥的端点，如果你使用JWT令牌的话
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                .tokenKeyAccess("permitAll()") // 公开 /oauth/token_key接口
                .checkTokenAccess("permitAll()") // 公开 /oauth/check_token 接口检测令牌
                .allowFormAuthenticationForClients(); // 允许表单认证
    }

    @Bean
    public AuthorizationServerTokenServices tokenServices(JwtAccessTokenConverter accessTokenConverter) {
        DefaultTokenServices services = new DefaultTokenServices();
        services.setClientDetailsService(clientDetailService);
        services.setSupportRefreshToken(true); // 是否产生刷新令牌
        services.setTokenStore(tokenStore);

        // 令牌增强
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Collections.singletonList(accessTokenConverter));
        services.setTokenEnhancer(tokenEnhancerChain);

        services.setAccessTokenValiditySeconds(7200);
        services.setRefreshTokenValiditySeconds(259200);
        return services;
    }
}
